#include <stdio.h>
#include <sys/mman.h>
#include <stddef.h>
#include <string.h>
#include <time.h>
#include <math.h>
#include <unistd.h>
#include <stdlib.h>
#include <ctype.h>
#include <getopt.h>
#include <time.h>
#include <sched.h>
#include <pthread.h>
#include <ctype.h>

#include "detection/paging.h"
#include "util/error.h"
#include "util/colorprint.h"

#include "cnet.h"
#include "jag/common.h"
#include "jag/send.h"
#include "jag/receive.h"
#include "detection/cache.h"
#include "detection/cpu.h"
#include "util/getopt_helper.h"
#include <net/transfer.h>
#include <libgen.h>

//enum { SPEEDUP_FACTOR = 0.5 };
//enum { SPEEDUP_FACTOR = 2 };
//#define SPEEDUP_FACTOR 0.5
#define SPEEDUP_FACTOR 1

static size_t *usable_sets;

static getopt_arg_t options[] =
        {
                {"receive",    no_argument,       NULL, 'r', "Start in [c]receive mode[/c] (default: [m]send mode[/m])", NULL},
                {"threshold",  required_argument, NULL, 't', "Set the cache-miss threshold to [y]THRESHOLD[/y] (default: 280)",                             "THRESHOLD"},
                {"delay",      required_argument, NULL, 'd', "Increase the [m]jamming[/m] and [c]listening[/c] delays by factor [y]DELAY[/y] (default: 1)", "DELAY"},
                {"patience",   required_argument, NULL, 'p', "Sets the watchdog timeout to [y]SECONDS[/y] seconds (default: 10)",                           "SECONDS"},
                {"cache-size", required_argument, NULL, 'c', "Set the size of the L3 cache to [y]SIZE[/y] bytes (default: auto detect)",                    "SIZE"},
                {"ways",       required_argument, NULL, 'w', "Set the number of cache ways to [y]WAYS[/y] ways (default: auto detect)",                     "WAYS"},
                {"slices",     required_argument, NULL, 's', "Set the number of cache slices to [y]SLICES[/y] slices (default: auto detect)",               "SLICES"},
                {"channels",   required_argument, NULL, 'a', "Set the number of cache channels to establish (default: 8)",                                  "CHANNELS"},
                {"message",    required_argument, NULL, 'm', "Message to send",                                                                             "MESSAGE"},
                {"file",       required_argument, NULL, 'f', "File to send/receive",                                                                                "FILE"},
                {"no-color",   no_argument,       NULL, 'n', "Disable color output.",                                    NULL},
                {"verbose",    no_argument,       NULL, 'v', "Verbose mode",                                             NULL},
                {"help",       no_argument,       NULL, 'h', "Show this help.",                                          NULL},
                {NULL, 0,                         NULL, 0, NULL,                                                         NULL}
        };

void print_message_data(const uint8_t *data, int data_len, const char *action_description);
// 添加这些函数声明
void process_received_data(const uint8_t *data, int data_len);
void detect_file_type(const uint8_t *data, int data_len);
void save_to_file(const uint8_t *data, int data_len, const char *filename);


int main(int argc, char **argv) {
    int verbose = 0;
    watchdog_t watchdog_settings;
    cjag_config_t config = {
            .send = 1,
            .color_output = 1,
            .channels = 8, /* YZ */
            .cache_miss_threshold = 280,
            .cache_probe_count = 3,
            .jag_send_count = 5000 * 3,
            .jag_recv_count = 15000 * 3,
            .set_offset = 20,
            .timeout = 10,
            .packet_size = config.channels /* - 2 */,
            .watchdog = &watchdog_settings
    };

    cache_config_l3_t l3 = get_l3_info();
    config.cache_size = l3.size;
    config.cache_ways = l3.ways;
    config.cache_kill_count = config.cache_ways - 1;
    config.cache_slices = get_slices();


    struct option *long_options = getopt_get_long_options((getopt_arg_t *) options);
    ERROR_ON(!long_options, ERROR_OOM, config.color_output);

    uint8_t *send_bits     = NULL;
    const char *message    = NULL;
    const char *file_name  = NULL;

    int c;
    while ((c = getopt_long(argc, argv, ":c:t:w:rs:nhd:p:a:m:f:v", long_options, NULL)) != EOF) {
        switch (c) {
            case 'r':
                config.send = 0;
                break;
            case 'c':
                config.cache_size = atoi(optarg);
                break;
            case 't':
                config.cache_miss_threshold = atoi(optarg);
                break;
            case 'w':
                config.cache_ways = atoi(optarg);
                config.cache_kill_count = config.cache_ways - 1;
                break;
            case 's':
                config.cache_slices = atoi(optarg);
                break;
            case 'n':
                config.color_output = 1;
                break;
            case 'd':
                config.jag_send_count = (int) (config.jag_send_count * atof(optarg));
                config.jag_recv_count = (int) (config.jag_recv_count * atof(optarg));
                break;
            case 'p':
                config.timeout = atoi(optarg);
                break;
            case 'a':
                config.channels = atoi(optarg);
                break;
            case 'f':
                file_name = strdup(optarg);
                break;
            case 'm':
                message= strdup(optarg);
                break;
            case 'v':
                verbose = 1;
                break;
            case 'h':
                show_usage(argv[0], &config);
                return 0;
            case ':':
                printf_color(config.color_output, ERROR_TAG "Option [c]-%c[/c] requires an [y]argument[/y].\n",
                             optopt);
                printf("\n");
                show_usage(argv[0], &config);
                return 1;
            case '?':
                if (isprint(optopt)) {
                    printf_color(config.color_output, ERROR_TAG "[y]Unknown[/y] option [c]-%c[/c].\n", optopt);
                } else {
                    printf_color(config.color_output, ERROR_TAG "[y]Unknown[/y] option character [c]\\x%x[/c].\n",
                                 optopt);
                }
                printf("\n");
                show_usage(argv[0], &config);
                return 1;
            default:
                show_usage(argv[0], &config);
                return 0;
        }
    }

    free(long_options);

    show_welcome(&config);
    show_parameters(&config);
    //printf("packet_size=%d.\n", config.packet_size);

    ERROR_ON(config.cache_slices > 16, ERROR_TOO_MANY_CPUS, config.color_output);
    ERROR_ON(config.cache_slices < 1 || config.cache_slices > 8 || (config.cache_slices & (config.cache_slices - 1)),
             ERROR_SLICES_NOT_SUPPORTED, config.color_output);
    ERROR_ON(config.cache_ways < 2, ERROR_INVALID_WAYS, config.color_output);
    ERROR_ON(config.timeout <= 1, ERROR_TIMEOUT_TOO_LOW, config.color_output);

    ERROR_ON(config.jag_send_count <= 0 || config.jag_recv_count <= 0, ERROR_INVALID_SPEED, config.color_output);

    ERROR_ON(!has_huge_pages(), ERROR_NO_HUGEPAGES, config.color_output);

    int init = jag_init(&config);
    ERROR_ON(!init, ERROR_NO_CACHE_SETS, config.color_output);

    usable_sets = calloc(config.channels, sizeof(size_t));
    ERROR_ON(!usable_sets, ERROR_OOM, config.color_output);
    ERROR_ON(config.send && !message && !file_name, ERROR_INVALID_PARAMETERS, config.color_output);

    watchdog_start(config.watchdog, config.timeout, timeout, (void *) &config);

    void **addrs = malloc(config.cache_ways * config.channels * sizeof(void *));
    if (config.send) {
        printf_color(config.color_output, "[y]Send mode[/y]: start [m]jamming[/m]...\n\n");
        printf_color(config.color_output, "[g][ # ][/g] Jamming set...\n");
        watchdog_reset(config.watchdog);
        jag_send(&config, send_callback);
        if(verbose) {
            printf_color(config.color_output, "[y]Eviction sets[/y]:\n");
            print_eviction_sets(config.addr, &config);
        }

        printf_color(config.color_output, "\n[y]Verification mode[/y]: start [c]receiving[/c]...\n\n");
        watchdog_reset(config.watchdog);
        sleep(1);
        watchdog_reset(config.watchdog);
        sleep(1);
        for (int i = 0; i < 15; i++) {
            watchdog_reset(config.watchdog);
            sched_yield();
        }

        printf_color(config.color_output, "[g][ # ][/g] Checking sets...\n");
        watchdog_reset(config.watchdog);
        jag_receive(addrs, usable_sets, &config, watch_callback);
        if(verbose) {
            printf_color(config.color_output, "[y]Eviction sets[/y]:\n");
            print_eviction_sets(addrs, &config);
        }

        int correct = 0;
        for (int i = 0; i < config.channels; i++) {
            if (i == usable_sets[i] - config.set_offset) {
                correct++;
            }
        }

        printf_color(config.color_output,
                     "\n[g][ # ][/g] Covert channels setup finished. [g]%.2f%%[/g] of the channels are established, your system is [r]%s[/r][y]%s[/y].\n\n",
                     (correct * 100.0 / config.channels), correct ? "[ V U L N E R A B L E ]" : "",
                     correct ? "" : "[ maybe not vulnerable ]");

	// 在调用strlen之前添加检查
	//if (!message) {
    	//	printf_color(config.color_output, ERROR_TAG "Message is NULL!\n");
    	//	return 1;
	//}
	if(file_name){
	    printf("File mode: %s\n", file_name);
        
            // 1. 打开文件
            FILE* file = fopen(file_name, "rb");  // 二进制模式读取
            if (!file) {
                printf_color(config.color_output, ERROR_TAG "Cannot open file: %s\n", file_name);
                return 1;
            }
        
            // 2. 获取文件大小
            fseek(file, 0, SEEK_END);
            long file_size = ftell(file);
            fseek(file, 0, SEEK_SET);
        
            if (file_size <= 0) {
                printf_color(config.color_output, ERROR_TAG "File is empty or error getting size: %s\n", file_name);
                fclose(file);
                return 1;
            }
        
            printf("File size: %ld bytes\n", file_size);
        
            // 3. 检查文件大小限制（避免发送过大文件）
            if (file_size > 10 * 1024 * 1024) {  // 10MB限制
                printf_color(config.color_output, ERROR_TAG "File too large: %ld bytes (max 10MB)\n", file_size);
                fclose(file);
                return 1;
            }
        
            // 4. 读取文件内容
            uint8_t* file_data = malloc(file_size);
            if (!file_data) {
                printf_color(config.color_output, ERROR_TAG "Memory allocation failed for file data\n");
                fclose(file);
                return 1;
            }
        
            size_t bytes_read = fread(file_data, 1, file_size, file);
            fclose(file);
        
            if (bytes_read != file_size) {
                printf_color(config.color_output, ERROR_TAG "File read error: read %zu bytes, expected %ld\n", 
                        bytes_read, file_size);
                free(file_data);
                return 1;
            }
        
            // 5. 发送文件内容
            printf("Sending file content...\n");
            time_t t1 = time(NULL);
            config.jag_send_count /= SPEEDUP_FACTOR;
        
            // 发送文件数据（不包含null终止符，因为可能是二进制文件）
            send_packet(&config, file_data, file_size);
        
            time_t t2 = time(NULL);
            printf("Sent %ld bytes for %d seconds, speed = %g bytes/sec.\n", 
               file_size, (int)(t2 - t1), (double)file_size / (t2 - t1));
        
            // 6. 清理内存
            free(file_data);
	}
	else if(message) {   
            int data_len = strlen(message) + 1;
            print_message_data((const uint8_t*)message, data_len, "Sending");
            time_t t1 = time(NULL);
            config.jag_send_count /= SPEEDUP_FACTOR;
            send_packet(&config, (const uint8_t*)message, data_len);
            time_t t2 = time(NULL);
            printf("Sent %d bytes for %d seconds, speed = %g bytes/sec.\n", (int)data_len, (int)(t2 - t1), (double)data_len / (t2 - t1));
	}else{
	    printf_color(config.color_output, ERROR_TAG "No message or file specified\n");
            return 1;
	}
	watchdog_done(config.watchdog);
    } else {
        printf_color(config.color_output, "[y]Receive mode[/y]: start [c]listening[/c]...\n\n");
        printf_color(config.color_output, "[g][ # ][/g] Receiving sets...\n");
        watchdog_reset(config.watchdog);
        jag_receive(addrs, usable_sets, &config, receive_callback);
        if(verbose) {
            printf_color(config.color_output, "[y]Eviction sets[/y]:\n");
            print_eviction_sets(addrs, &config);
        }

        printf_color(config.color_output, "\n[g][ # ][/g] Reconstructing mapping...\n");
        for (int i = 0; i < config.channels; i++) {
            printf_color(config.color_output, "[g][ + ][/g]   Sender[[m]%d[/m]] -> Receiver[[c]%zd[/c]]\n", i,
                         usable_sets[i]);
        }

        printf_color(config.color_output, "\n[y]Test mode[/y]: start [m]probing[/m]...\n\n");
        watchdog_reset(config.watchdog);
        sleep(1);
        for (int i = 0; i < 15; i++) {
            watchdog_reset(config.watchdog);
            sched_yield();
        }

        watchdog_reset(config.watchdog);
        void *old_addr = config.addr;
        config.addr = addrs; // send back the received addresses
        jag_send(&config, probe_callback);
        config.addr = old_addr; // bit of a hack to prevent double free and memory leak...
        printf_color(config.color_output, "\n[g][ # ][/g] Covert channels setup succeed!\n\n");

        //sleep(3); /* wait for sender */
        //printf("Press any key to start receiving...");
        //getchar();

#if 0
        watchdog_reset(config.watchdog);
        sleep(1);
        watchdog_reset(config.watchdog);
        sleep(1);
        for (int i = 0; i < 15; i++) {
            watchdog_reset(config.watchdog);
            sched_yield();
        }
#endif
        //uint8_t dst_buf[4096];
	uint8_t dst_buf[64 * 1024]; // 增大缓冲区到64KB以支持文件传输
        time_t t1 = time(NULL);
        config.jag_recv_count /= SPEEDUP_FACTOR;
        int data_size = receive_packet(&config, usable_sets, dst_buf, sizeof(dst_buf));
        time_t t2 = time(NULL);
        printf("Received %d bytes for %d seconds, speed = %g bytes/sec.\n", (int)data_size, (int)(t2 - t1), (double)data_size / (t2 - t1));
        //print_message_data(dst_buf, data_size, "Received");

        // 智能判断数据类型并处理   
	//printf("%s\n", file_name); 
        //const char* path = file_name;
        //const char* filename = basename(path); 
        //printf("%s\n", filename);
	process_received_data(dst_buf, data_size);
        
	printf("Transfer finished.\n");
        watchdog_done(config.watchdog);

    }

    ERROR_ON(!jag_free(&config), ERROR_UNMAP_FAILED, config.color_output);
    free(usable_sets);
    free(addrs);

    return 0;
}


void show_welcome(cjag_config_t *config) {
    printf_color(config->color_output,
                 "\n___[y]/\\/\\/\\/\\/\\[/y]__________[r]/\\/\\[/r]______[r]/\\/\\[/r]________[r]/\\/\\/\\/\\/\\[/r]_\n");
    printf_color(config->color_output,
                 "_[y]/\\/\\[/y]__________________[r]/\\/\\[/r]____[r]/\\/\\/\\/\\[/r]____[r]/\\/\\[/r]_________\n");
    printf_color(config->color_output,
                 "_[y]/\\/\\[/y]__________________[r]/\\/\\[/r]__[r]/\\/\\[/r]____[r]/\\/\\[/r]__[r]/\\/\\[/r]__[r]/\\/\\/\\[/r]_\n");
    printf_color(config->color_output,
                 "_[y]/\\/\\[/y]__________[r]/\\/\\[/r]____[r]/\\/\\[/r]__[r]/\\/\\/\\/\\/\\/\\[/r]__[r]/\\/\\[/r]____[r]/\\/\\[/r]_\n");
    printf_color(config->color_output,
                 "___[y]/\\/\\/\\/\\/\\[/y]____[r]/\\/\\/\\/\\[/r]____[r]/\\/\\[/r]____[r]/\\/\\[/r]____[r]/\\/\\/\\/\\/\\[/r]_\n");
    printf_color(config->color_output, "________________________________________________________\n\n");
}

void show_parameters(cjag_config_t *config) {
    int size = config->cache_size / 1024;
    const char *unit = "KB";
    if (size >= 1024 && size % 1024 == 0) {
        size /= 1024;
        unit = "MB";
    }
    printf_color(config->color_output, "      %10s %10s %10s %10s\n", "Size", "Ways", "Slices", "Threshold");
    printf_color(config->color_output, "L3    [g]%7d %s[/g] [g]%9d[/g]  [g]%7d[/g]  [g]%9d[/g]\n\n", size, unit,
                 config->cache_ways, config->cache_slices, config->cache_miss_threshold);
}

void show_usage(char *binary, cjag_config_t *config) {
    printf_color(config->color_output, "[y]USAGE[/y]\n  %s [g][options][/g] \n\n", binary);
    getopt_arg_t null_option = {0};
    size_t count = 0;
    printf_color(config->color_output, "\n[y]OPTIONS[/y]\n");
    do {
        if (!memcmp((void *) &options[count], (void *) &null_option, sizeof(getopt_arg_t))) {
            break;
        } else if (options[count].description) {
            printf_color(config->color_output, "  [g]-%c[/g]%s[y]%s[/y]%s [g]%s%s%s[/g][y]%s[/y]\n     ",
                         options[count].val,
                         options[count].has_arg != no_argument ? " " : "",
                         options[count].has_arg != no_argument ? options[count].arg_name : "",
                         options[count].name ? "," : "", options[count].name ? "--" : "", options[count].name,
                         options[count].has_arg != no_argument ? "=" : "",
                         options[count].has_arg != no_argument ? options[count].arg_name : "");
            printf_color(config->color_output, options[count].description);
            printf_color(config->color_output, "\n\n");
        }
        count++;
    } while (1);
    printf("\n");
    printf_color(config->color_output,
                 "[y]EXAMPLE[/y]\n  Start [m]%s[/m] and [c]%s --receive[/c] in two different terminals (or on two co-located VMs).\n\n\n",
                 binary, binary);
    printf_color(config->color_output,
                 "[y]AUTHORS[/y]\n  Written by Michael Schwarz, Lukas Giner, Daniel Gruss, Clémentine Maurice and Manuel Weber.\n\n");

}

void send_callback(cjag_config_t *config, int set) {
    watchdog_reset(config->watchdog);
    printf_color(config->color_output, "[g][ + ][/g]   ...set #%d\n", set);
}

void probe_callback(cjag_config_t *config, int set) {
    watchdog_reset(config->watchdog);
    printf_color(config->color_output, "[g][ + ][/g] Probing set #%d ([[c]%d[/c]] -> [[m]%d[/m]])\n", set,
                 usable_sets[set - 1], set - 1);
}

void receive_callback(cjag_config_t *config, int set) {
    watchdog_reset(config->watchdog);
    printf_color(config->color_output, "[g][ + ][/g]   ...set #%d\n", set);
}

void watch_callback(cjag_config_t *config, int set) {
    watchdog_reset(config->watchdog);
    printf_color(config->color_output, "[g][ + ][/g]   Sender[[m]%d[/m]] <-> Sender[[c]%zd[/c]] [g]%s[/g][r]%s[/r]\n",
                 set - 1, usable_sets[set - 1] - config->set_offset,
                 (set - 1 == usable_sets[set - 1] - config->set_offset) ? "[ OK ]" : "",
                 (set - 1 != usable_sets[set - 1] - config->set_offset) ? "[FAIL]" : "");
}

void timeout(void *arg) {
    cjag_config_t *config = (cjag_config_t *) arg;

    printf_color(config->color_output,
                 ERROR_TAG "Timeout after [g]%d seconds[/g].\n        Please [y]try to restart[/y] the application.\n\n",
                 config->timeout);
    exit(1);
}

void print_eviction_sets(void** addr, cjag_config_t* config) {
    for (int i = 0; i < config->channels; i++) {
        printf_color(config->color_output, "[g][ . ] Set #%d[/g]\n", i + 1);
        for (int j = 0; j < config->cache_ways; j++) {
            void* v_addr = GET_EVICTION_SET(addr, i, config)[j];
            printf_color(config->color_output, "  %p\n", v_addr);
        }
    }
}

/* prints data either as string or as hex data */
void print_message_data(const uint8_t *data, int data_len, const char *action_description) {
    const char *message = (const char*)data;

    printf("%s data: ", action_description);
    int is_string = 1;
    for (int i = 0; i < data_len; i++) {
        if (!isascii(message[i]) || ((i == data_len - 1) && message[i] != 0)) {
            is_string = 0;
            break;
        }
    }
    if (is_string) {
        printf("\"%s\"\n", (const char*)data);
    }
    else {
        printf("[ ");
        for (size_t i = 0; i < data_len; i++) {
            printf("0x%02x ", (unsigned)data[i]);
        }
        printf("]\n");
    }
}


/**
 * 智能处理接收到的数据
 * 自动判断是文本消息还是文件数据
 */
void process_received_data(const uint8_t *data, int data_len) {
    if (data_len <= 0) {
        printf("No data received.\n");
        return;
    }
   
    // 检查数据是否为文本
    int is_text = 1;
    int has_null_terminator = (data[data_len - 1] == 0);
    int printable_count = 0;
    
    //for(int i = 0;i<data_len - 1;i++){
    //    printf("%c", data[i]);
    //}

    for (int i = 0; i < data_len && i < 1000; i++) {
	//printf("%c", data[i]);
        if (i < (data_len - 1) && data[i] == 0) {
            // 中间有null字符，可能是二进制数据
	    //printf("%d", i);
            is_text = 0;
            break;
        }
        if (isprint(data[i]) || isspace(data[i])) {
            printable_count++;
        }
    }

    //printf("is_text = %d", is_text);
    // 如果95%以上的字符是可打印的，认为是文本
    //printf("pc = %d", printable_count);
    float printable_ratio = (float)printable_count / (data_len < 1000 ? data_len - 1 : 1000);
    //printf("ratio = %lf", printable_ratio);
    is_text = is_text && (printable_ratio > 0.9);

    //printf("is_text = %d, hnt = %d", is_text, has_null_terminator);
    if (is_text && has_null_terminator) {
        // 文本消息
        printf("Received message: \"%s\"\n", (const char*)data);
    }
    else if (is_text) {
        // 文本但没有null终止符，可能是文件内容
        printf("Received text data (%d bytes):\n", data_len);
        printf("```\n");
        // 安全打印文本内容
        for (int i = 0; i < data_len && i < 1000; i++) {
            if (isprint(data[i]) || isspace(data[i])) {
                putchar(data[i]);
            } else if (data[i] == 0) {
                break; // 遇到null停止
            } else {
                printf("\\x%02x", data[i]); // 非打印字符转义
            }
        }
        printf("\n```\n");

        // 询问是否保存为文件
        printf("Save as file? (y/n): ");
        fflush(stdout);
        char response = getchar();
        if (response == 'y' || response == 'Y') {
	    // 自动生成文件名，不依赖外部变量
            char auto_filename[64];
	    snprintf(auto_filename, sizeof(auto_filename), "received_text_%ld.txt", (long)time(NULL));
            printf("Saved as: %s\n", auto_filename);
	    save_to_file(data, data_len, auto_filename);
        }
    }
    else {
        // 二进制数据，可能是文件
        printf("Received binary data (%d bytes)\n", data_len);

        // 显示前100字节的hex dump
        printf("Hex dump (first 100 bytes):\n");
        for (int i = 0; i < data_len && i < 100; i++) {
            printf("%02x ", data[i]);
            if ((i + 1) % 16 == 0) printf("\n");
        }
        printf("\n");

        // 尝试检测文件类型
        detect_file_type(data, data_len);

        // 自动保存为文件
        char filename[64];
        snprintf(filename, sizeof(filename), "received_%ld.bin", (long)time(NULL));
        save_to_file(data, data_len, filename);
    }
}

/**
 * 检测文件类型
 */
void detect_file_type(const uint8_t *data, int data_len) {
    if (data_len >= 4) {
        // 检查常见的文件魔数
        if (data[0] == 0x25 && data[1] == 0x50 && data[2] == 0x44 && data[3] == 0x46) {
            printf("File type detected: PDF\n");
        }
        else if (data[0] == 0x50 && data[1] == 0x4B && data[2] == 0x03 && data[3] == 0x04) {
            printf("File type detected: ZIP\n");
        }
        else if (data[0] == 0x89 && data[1] == 0x50 && data[2] == 0x4E && data[3] == 0x47) {
            printf("File type detected: PNG\n");
        }
        else if (data[0] == 0xFF && data[1] == 0xD8 && data[2] == 0xFF) {
            printf("File type detected: JPEG\n");
        }
        else if (strncmp((const char*)data, "PK", 2) == 0) {
            printf("File type detected: ZIP (alternative signature)\n");
        }
        else if (strncmp((const char*)data, "%PDF", 4) == 0) {
            printf("File type detected: PDF (text signature)\n");
        }
        else {
            printf("File type: Unknown binary\n");
        }
    }
}

/**
 * 保存数据到文件
 */
void save_to_file(const uint8_t *data, int data_len, const char *filename) {
    FILE *file = fopen(filename, "wb");
    printf("%s", filename);
    if (!file) {
        printf("Error: Cannot create file %s\n", filename);
        return;
    }

    size_t written = fwrite(data, 1, data_len, file);
    fclose(file);

    if (written == data_len) {
        printf("Data saved to: %s (%d bytes)\n", filename, data_len);
    } else {
        printf("Error: Only wrote %zu of %d bytes to %s\n", written, data_len, filename);
    }
}
